using System;
using System.Collections;
using System.Collections.Generic;


namespace NodeBasedSkillEditor
{
    public class RT_Node
    {
        public NodeData SourceData { get; private set; }
        public RT_DAG ParentDag { get; private set; }
        public uint Id { get { return SourceData.LocalId; } }
        /// <summary>
        /// key = portLocalId, value = runtime port
        /// </summary>
        public Dictionary<uint, RT_Port> AllPorts { get; private set; } = new Dictionary<uint, RT_Port>();

        public RT_Node(NodeData sourceData, RT_DAG parentDag)
        {
            SourceData = sourceData;
            ParentDag = parentDag;
        }

        public RT_Port GetPort(uint portLocalId)
        {
            RT_Port result;
            AllPorts.TryGetValue(portLocalId, out result);
            return result;
        }

        public void InitializePortLinks()
        {
            foreach (var p in AllPorts)
            {
                var rtPort = p.Value;
                if (rtPort.IOType == EPortIOType.Input
                    && rtPort.WorkDirection == RT_Port.EWorkingDirection.Backward)
                {
                    HashSet<uint> linkedPortIds;
                    ParentDag.Links.TryGetBinds(p.Value.Id, out linkedPortIds);
                    if (linkedPortIds != null && linkedPortIds.Count > 0)
                    {
                        foreach (var lpId in linkedPortIds)
                        {
                            var linkedPort = ParentDag.GetPort(lpId);
                            rtPort.LinkedPortInWorkingDirection = linkedPort;
                            break;
                        }
                    }
                }
                else if (rtPort.IOType == EPortIOType.Output
                    && rtPort.WorkDirection == RT_Port.EWorkingDirection.Foreward)
                {
                    HashSet<uint> linkedPortIds;
                    ParentDag.Links.TryGetBinds(p.Value.Id, out linkedPortIds);
                    if (linkedPortIds != null && linkedPortIds.Count > 0)
                    {
                        foreach(var lpId in linkedPortIds)
                        {
                            var linkedPort = ParentDag.GetPort(lpId);
                            rtPort.LinkedPortInWorkingDirection = linkedPort;
                            break;
                        }
                    }
                }
            }
        }

        public virtual (EDagExeState, RT_Node) ForewardExecute(DAG_Executor executor) { return (EDagExeState.Backward, null); }

        public virtual (EDagExeState, RT_Node) UpdateExecute(DAG_Executor executor) { return (EDagExeState.Backward, null); }

        public virtual (EDagExeState, RT_Node) BackwardExecute(DAG_Executor executor) { return (EDagExeState.Backward, null); }

        public (EDagExeState, RT_Node) TryForewardByPort(RT_FlowPort forewardPort)
        {
            var resultNode = forewardPort.LinkedPortInWorkingDirection != null ? forewardPort.LinkedPortInWorkingDirection.ParentNode : null;
            var exeState = resultNode != null ? EDagExeState.Foreward : EDagExeState.Backward;
            return (exeState, resultNode);
        }

        protected T[] BuildRTPortArrayFromGroup<T>(EPortIOType ioType, PortGroup group) where T : RT_Port
        {
            T[] rtPorts = new T[group.Ports.Count];

            for (int i = 0; i < group.Ports.Count; i++)
            {
                var currPortData = group.Ports[i];
                var typeT = typeof(T);
                T newRTPort = null;
                if (typeT == typeof(RT_FlowPort))
                {
                    newRTPort = new RT_FlowPort(ioType, currPortData, this) as T;
                }
                else if (typeT == typeof(RT_SingleValuePort))
                {
                    newRTPort = new RT_SingleValuePort(ioType, currPortData, this) as T;
                }
                else if (typeT == typeof(RT_ValueListPort))
                {
                    newRTPort = new RT_ValueListPort(ioType, currPortData, this) as T;
                }
                rtPorts[i] = newRTPort;

            }

            return rtPorts;
        }

    }

    public abstract class RT_NodeExeInfo
    {

    }


}
